package com.hejia.alauda.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hejia.alauda.exception.BusinessException;

/**
 * 连连支付常用工具函数
 * 
 * @author guoyx e-mail:guoyx@lianlian.com
 * @date:2013-6-25 下午05:23:05
 * @version :1.0
 *
 */
public class LLPayUtil {
	private static final Log logger = LogFactory.getLog(LLPayUtil.class);
	private static final int TIMEOUT = 10000;

	/**
	 * 银行卡解约
	 * 
	 * @param external
	 * @param agreeno
	 * @return
	 */
	public static boolean bankcardUnbind(String external, String agreeno)
			throws BusinessException {
		String merid = LLPayConfig.getInstance().getValue("llpay.pay.merid");
		String md5key = LLPayConfig.getInstance().getValue("llpay.pay.md5key");
		String signType = LLPayConfig.getInstance().getValue("llpay.sign.type");
		String merkey = LLPayConfig.getInstance().getValue("llpay.pay.merkey");
		JSONObject reqJSON = JSONObject.parseObject("{}");
		reqJSON.put("oid_partner", merid);
		reqJSON.put("user_id", external);
		reqJSON.put("sign_type", signType);
		reqJSON.put("no_agree", agreeno);
		reqJSON.put("pay_type", "D");
		String sign = LLPayUtil.addSign(reqJSON, merkey, md5key);
		reqJSON.put("sign", sign);

		logger.debug("银行卡解约请求" + reqJSON.toJSONString());
		
		CloseableHttpClient httpclient = HttpClients.createDefault();

		HttpPost httpPost = new HttpPost(
				"https://yintong.com.cn/traderapi/bankcardunbind.htm");
		httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
		try {
			RequestConfig config = RequestConfig.custom()
					.setConnectionRequestTimeout(TIMEOUT)
					.setConnectTimeout(TIMEOUT).setSocketTimeout(TIMEOUT)
					.build();
			httpPost.setConfig(config);
			StringEntity se = new StringEntity(reqJSON.toJSONString(), "UTF-8");
			se.setContentType("text/json");
			se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
					"text/json"));
			httpPost.setEntity(se);
			CloseableHttpResponse response1 = httpclient.execute(httpPost);
			HttpEntity entity1 = response1.getEntity();
			if (entity1 != null) {
				String response = EntityUtils.toString(entity1, "UTF-8");
				JSONObject jsonResp = JSONObject.parseObject(response);

				logger.debug("银行卡解约结果：" + jsonResp.toJSONString());

				if (!"0000".equals(jsonResp.get("ret_code").toString())) {
					logger.debug("银行卡解约失败："
							+ jsonResp.get("ret_msg").toString());
					throw new BusinessException("银行卡解约失败："
							+ jsonResp.get("ret_msg").toString());
				}
				return true;
			}
		} catch (BusinessException e) {
			throw e;
		} catch (Exception e) {
			logger.error("连连银行卡解约异常:" + e.toString());
		}
		return false;
	}

	/**
	 * 连连银行卡签约号查询
	 * 
	 * @param external
	 *            外部用户ID
	 * @param cardNumber
	 *            银行卡卡号
	 * @return
	 */
	public static String queryAgreeNo(String external, String cardNumber) {
		String merid = LLPayConfig.getInstance().getValue("llpay.pay.merid");
		String md5key = LLPayConfig.getInstance().getValue("llpay.pay.md5key");
		String signType = LLPayConfig.getInstance().getValue("llpay.sign.type");
		String merkey = LLPayConfig.getInstance().getValue("llpay.pay.merkey");
		JSONObject reqJSON = JSONObject.parseObject("{}");
		reqJSON.put("oid_partner", merid);
		reqJSON.put("user_id", external);
		reqJSON.put("sign_type", signType);
		reqJSON.put("card_no", cardNumber);
		reqJSON.put("pay_type", "D");
		reqJSON.put("offset", "0");
		String sign = LLPayUtil.addSign(reqJSON, merkey, md5key);
		reqJSON.put("sign", sign);

		logger.debug("连连银行卡签约号查询请求" + reqJSON.toJSONString());
		
		CloseableHttpClient httpclient = HttpClients.createDefault();

		HttpPost httpPost = new HttpPost(
				"https://yintong.com.cn/traderapi/userbankcard.htm");
		try {
			RequestConfig config = RequestConfig.custom()
					.setConnectionRequestTimeout(TIMEOUT)
					.setConnectTimeout(TIMEOUT).setSocketTimeout(TIMEOUT)
					.build();
			httpPost.setConfig(config);
			httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");

			StringEntity se = new StringEntity(reqJSON.toJSONString(), "UTF-8");
			se.setContentType("text/json");
			se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
					"text/json"));
			httpPost.setEntity(se);
			CloseableHttpResponse response1 = httpclient.execute(httpPost);
			HttpEntity entity1 = response1.getEntity();
			if (entity1 != null) {
				String response = EntityUtils.toString(entity1, "UTF-8");
				JSONObject jsonResp = JSONObject.parseObject(response);

				logger.debug("签约信息查询结果：" + jsonResp.toJSONString());

				if (!"0000".equals(jsonResp.get("ret_code").toString())) {
					logger.debug("签约信息查询失败："
							+ jsonResp.get("ret_msg").toString());
					return null;
				}

				JSONArray arr = (JSONArray) jsonResp.get("agreement_list");
				String noagree = (String) ((JSONObject) arr.get(0))
						.get("no_agree");

				// if (!LLPayUtil.checkSign(response, pubkey, md5key)) {
				// logger.debug("商户代付失败：签名认证验证失败!");
				// return ;
				// }
				return noagree;
			}
		} catch (Exception e) {
			logger.error("连连银行卡签约号查询异常:" + e.toString());
		}
		return null;
	}

	/**
	 * 商户支付结果查询
	 * 
	 * @param orderNo
	 *            支付订单
	 * @return JSONObject{"ret_code":"0000","ret_msg":"交易成功","result_pay":
	 *         "支付结果:SUCCESS 成功 WAITING 等待支付 PROCESSING 银行支付处理中 REFUND 退款 FAILURE 失败"
	 *         ,"oid_paybill":"连连支付支付单号","bank_code":"银行编号","bank_name":"银行名称",
	 *         "settle_date":"清算日期","pay_type":
	 *         "支付方式:0:余额支付 2:快捷支付(借记卡) 3:快捷支付(信用卡) D:认证支付(借记卡) E:认证支付(信用卡)" }
	 */
	public static JSONObject orderQuery(String orderNo) {
		String merid = LLPayConfig.getInstance().getValue("llpay.pay.merid");
		String md5key = LLPayConfig.getInstance().getValue("llpay.pay.md5key");
		String signType = LLPayConfig.getInstance().getValue("llpay.sign.type");
		String merkey = LLPayConfig.getInstance().getValue("llpay.pay.merkey");
		String pubkey = LLPayConfig.getInstance().getValue("llpay.pay.pubkey");
		JSONObject reqJSON = JSONObject.parseObject("{}");
		reqJSON.put("oid_partner", merid);
		reqJSON.put("sign_type", signType);
		reqJSON.put("no_order", orderNo);
		String sign = LLPayUtil.addSign(reqJSON, merkey, md5key);
		reqJSON.put("sign", sign);

		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost(
				"https://yintong.com.cn/traderapi/orderquery.htm");
		httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
		try {
			RequestConfig config = RequestConfig.custom()
					.setConnectionRequestTimeout(TIMEOUT)
					.setConnectTimeout(TIMEOUT).setSocketTimeout(TIMEOUT)
					.build();
			httpPost.setConfig(config);
			StringEntity se = new StringEntity(reqJSON.toJSONString());
			se.setContentType("text/json");
			se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
					"text/json"));
			httpPost.setEntity(se);
			CloseableHttpResponse response1 = httpclient.execute(httpPost);
			HttpEntity entity1 = response1.getEntity();
			if (entity1 != null) {
				String response = EntityUtils.toString(entity1, "UTF-8");
				JSONObject jsonResp = JSONObject.parseObject(response);
				if (!"0000".equals(jsonResp.get("ret_code").toString())) {
					logger.debug("商户支付结果查询失败：" + jsonResp.get("ret_msg").toString());
					return null;
				}

				JSONObject checkSignJson = JSONObject.parseObject(response);
				checkSignJson.remove("card_no"); // 银行卡号不参与签名，需去掉
				if (!LLPayUtil.checkSign(checkSignJson.toJSONString(), pubkey, md5key)) {
					logger.debug("商户支付结果查询失败：签名认证验证失败!");
					return null;
				}

				return jsonResp;
			}
		} catch (Exception e) {
			logger.error("连连商户支付结果查询异常", e);
		}
		return null;
	}

	/**
	 * 判断是否支持支付认证银行卡
	 * 
	 * @param cardNo
	 *            银行卡号
	 * @return 返回认证JSONObject对象： 认证失败返回{"status":false,"msg":"失败原因"}，
	 *         认证成功返回{"status" :true,"bankcode":"银行编码","bankname":"银行名称"}
	 */
	public static JSONObject validateBin(String cardNo) {
		JSONObject rest = JSONObject.parseObject("{}");
		JSONObject jsonResp = bankcardQuery(cardNo);
		if (jsonResp == null) {
			rest.put("status", false);
			rest.put("msg", "卡bin查询失败");
			return rest;
		}

		if (!"2".equals(jsonResp.get("card_type").toString())) {
			rest.put("status", false);
			rest.put("msg", "不支持该银行卡");
			return rest;
		} else {
			rest.put("status", true);
			rest.put("bankcode", jsonResp.get("bank_code").toString());
			rest.put("bankname", jsonResp.get("bank_name").toString());
			return rest;
		}
	}

	/**
	 * 查询银行bin
	 * 
	 * @param cardNo
	 * @return 查询正确返回银行名称,失败返回null
	 */
	public static String queryBankName(String cardNo) {

		JSONObject jsonResp = bankcardQuery(cardNo);
		if (jsonResp == null) {
			return null;
		}
		return jsonResp.get("bank_name").toString();
	}

	/**
	 * 查询银行编码
	 * 
	 * @param cardNo
	 * @return 查询正确返回银行名称,失败返回null
	 */
	public static String queryBankCode(String cardNo) {
		JSONObject jsonResp = bankcardQuery(cardNo);
		if (jsonResp == null) {
			return null;
		}
		return jsonResp.get("bank_code").toString();
	}

	public static JSONObject bankcardQuery(String cardNo) {
		String merid = LLPayConfig.getInstance().getValue("llpay.pay.merid");
		String md5key = LLPayConfig.getInstance().getValue("llpay.pay.md5key");
		String signType = LLPayConfig.getInstance().getValue("llpay.sign.type");
		String merkey = LLPayConfig.getInstance().getValue("llpay.pay.merkey");
		String pubkey = LLPayConfig.getInstance().getValue("llpay.pay.pubkey");
		JSONObject reqJSON = JSONObject.parseObject("{}");
		reqJSON.put("oid_partner", merid);
		reqJSON.put("card_no", cardNo);
		reqJSON.put("sign_type", signType);

		String sign = LLPayUtil.addSign(reqJSON, merkey, md5key);
		reqJSON.put("sign", sign);

		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost(
				"https://yintong.com.cn/traderapi/bankcardquery.htm");
		httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
		try {
			RequestConfig config = RequestConfig.custom()
					.setConnectionRequestTimeout(TIMEOUT)
					.setConnectTimeout(TIMEOUT).setSocketTimeout(TIMEOUT)
					.build();
			httpPost.setConfig(config);
			StringEntity se = new StringEntity(reqJSON.toJSONString());
			se.setContentType("text/json");
			se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
					"text/json"));
			httpPost.setEntity(se);
			CloseableHttpResponse response1 = httpclient.execute(httpPost);
			HttpEntity entity1 = response1.getEntity();
			if (entity1 != null) {
				String response = EntityUtils.toString(entity1, "UTF-8");
				JSONObject jsonResp = JSONObject.parseObject(response);
				if (!"0000".equals(jsonResp.get("ret_code").toString())) {
					logger.debug("查询银行BIN失败："
							+ jsonResp.get("ret_msg").toString());
					return null;
				}

				if (!LLPayUtil.checkSign(response, pubkey, md5key)) {
					logger.debug("查询银行BIN失败：签名认证验证失败!");
					return null;
				}

				return jsonResp;
			}
		} catch (Exception e) {
			logger.error("查询银行BIN失败：" + e.toString());
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * str空判断
	 * 
	 * @param str
	 * @return
	 * @author guoyx
	 */
	public static boolean isnull(String str) {
		if (null == str || str.equalsIgnoreCase("null") || str.equals("")) {
			return true;
		} else
			return false;
	}

	/**
	 * 获取当前时间str，格式yyyyMMddHHmmss
	 * 
	 * @return
	 * @author guoyx
	 */
	public static String getCurrentDateTimeStr() {
		SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMddHHmmss");
		Date date = new Date();
		String timeString = dataFormat.format(date);
		return timeString;
	}

	/**
	 * 
	 * 功能描述：获取真实的IP地址
	 * 
	 * @param request
	 * @return
	 * @author guoyx
	 */
	public static String getIpAddr(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		if (!isnull(ip) && ip.contains(",")) {
			String[] ips = ip.split(",");
			ip = ips[ips.length - 1];
		}
		return ip;
	}

	/**
	 * 生成待签名串
	 * 
	 * @param paramMap
	 * @return
	 * @author guoyx
	 */
	public static String genSignData(JSONObject jsonObject) {
		StringBuffer content = new StringBuffer();

		// 按照key做首字母升序排列
		List<String> keys = new ArrayList<String>(jsonObject.keySet());
		Collections.sort(keys, String.CASE_INSENSITIVE_ORDER);
		for (int i = 0; i < keys.size(); i++) {
			String key = (String) keys.get(i);
			if ("sign".equals(key)) {
				continue;
			}
			String value = jsonObject.getString(key);
			// 空串不参与签名
			if (isnull(value)) {
				continue;
			}
			content.append((i == 0 ? "" : "&") + key + "=" + value);

		}
		String signSrc = content.toString();
		if (signSrc.startsWith("&")) {
			signSrc = signSrc.replaceFirst("&", "");
		}
		return signSrc;
	}

	/**
	 * 加签
	 * 
	 * @param reqObj
	 * @param rsa_private
	 * @param md5_key
	 * @return
	 * @author guoyx
	 */
	public static String addSign(JSONObject reqObj, String rsa_private,
			String md5_key) {
		if (reqObj == null) {
			return "";
		}
		String sign_type = reqObj.getString("sign_type");
		if ("MD5".equals(sign_type)) {
			return addSignMD5(reqObj, md5_key);
		} else {
			return addSignRSA(reqObj, rsa_private);
		}
	}

	/**
	 * 签名验证
	 * 
	 * @param reqStr
	 * @return
	 */
	public static boolean checkSign(String reqStr, String rsa_public,
			String md5_key) {
		JSONObject reqObj = JSON.parseObject(reqStr);
		if (reqObj == null) {
			return false;
		}

		String sign_type = reqObj.getString("sign_type");
		if ("MD5".equals(sign_type)) {
			return checkSignMD5(reqObj, md5_key);
		} else {
			return checkSignRSA(reqObj, rsa_public);
		}
	}

	/**
	 * RSA签名验证
	 * 
	 * @param reqObj
	 * @return
	 * @author guoyx
	 */
	private static boolean checkSignRSA(JSONObject reqObj, String rsa_public) {
		if (reqObj == null) {
			return false;
		}

		String sign = reqObj.getString("sign");
		// 生成待签名串
		String sign_src = genSignData(reqObj);
		try {
			if (TraderRSAUtil.checksign(rsa_public, sign_src, sign)) {
				return true;
			} else {
				return false;
			}
		} catch (Exception e) {
			logger.debug("商户[" + reqObj.getString("oid_partner") + "]RSA签名验证异常"
					+ e.getMessage());
			return false;
		}
	}

	/**
	 * MD5签名验证
	 * 
	 * @param signSrc
	 * @param sign
	 * @return
	 * @author guoyx
	 */
	private static boolean checkSignMD5(JSONObject reqObj, String md5_key) {
		if (reqObj == null) {
			return false;
		}
		String sign = reqObj.getString("sign");
		// 生成待签名串
		String sign_src = genSignData(reqObj);
		sign_src += "&key=" + md5_key;
		try {
			if (sign.equals(Encryption.encrypt(sign_src))) {
				return true;
			} else {
				return false;
			}
		} catch (Exception e) {
			logger.debug("商户[" + reqObj.getString("oid_partner") + "]MD5签名验证异常"
					+ e.getMessage());
			return false;
		}
	}

	/**
	 * RSA加签名
	 * 
	 * @param reqObj
	 * @param rsa_private
	 * @return
	 * @author guoyx
	 */
	private static String addSignRSA(JSONObject reqObj, String rsa_private) {
		if (reqObj == null) {
			return "";
		}
		// 生成待签名串
		String sign_src = genSignData(reqObj);
		try {
			logger.debug("待签名原字符串:" + sign_src);
			return TraderRSAUtil.sign(rsa_private, sign_src);
		} catch (Exception e) {
			logger.debug("商户[" + reqObj.getString("oid_partner") + "]RSA加签名异常"
					+ e.getMessage());
			return "";
		}
	}

	/**
	 * MD5加签名
	 * 
	 * @param reqObj
	 * @param md5_key
	 * @return
	 * @author guoyx
	 */
	private static String addSignMD5(JSONObject reqObj, String md5_key) {
		if (reqObj == null) {
			return "";
		}
		// 生成待签名串
		String sign_src = genSignData(reqObj);
		sign_src += "&key=" + md5_key;
		try {
			System.out.println("Sign_src:" + sign_src);
			return Encryption.encrypt(sign_src);
		} catch (Exception e) {
			logger.debug("商户[" + reqObj.getString("oid_partner") + "]MD5加签名异常"
					+ e.getMessage());
			return "";
		}
	}

	/**
	 * 读取request流
	 * 
	 * @param req
	 * @return
	 * @author guoyx
	 */
	public static String readReqStr(HttpServletRequest request) {
		BufferedReader reader = null;
		StringBuilder sb = new StringBuilder();
		try {
			reader = new BufferedReader(new InputStreamReader(
					request.getInputStream(), "utf-8"));
			String line = null;

			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != reader) {
					reader.close();
				}
			} catch (IOException e) {

			}
		}
		return sb.toString();
	}
	
	/**
	 * 格式化订单时间
	 * @param dateTime 时间
	 * @return
	 */
	public static String formatOrderDateTime(Date dateTime) {
		SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMddHHmmss");
		return dataFormat.format(dateTime);
	}
	
	/**
	 * 银行状态及限额查询
	 * @return
	 */
	public static JSONObject supportBankQuery() {
		
		String merid = LLPayConfig.getInstance().getValue("llpay.pay.merid");
		String md5key = LLPayConfig.getInstance().getValue("llpay.pay.md5key");
		String signType = LLPayConfig.getInstance().getValue("llpay.sign.type");
		String merkey = LLPayConfig.getInstance().getValue("llpay.pay.merkey");

		JSONObject reqJSON = JSONObject.parseObject("{}");
		reqJSON.put("oid_partner", merid);
		reqJSON.put("sign_type", signType);
		reqJSON.put("api_version", "1.0");
		reqJSON.put("product_type", "1"); // 产品类型：1 认证支付
		reqJSON.put("pay_chnl", "13"); // 支付渠道类型：10 手机SDK 13 PC端 WEB 15 API支付接口 16 手机WAP

		String sign = LLPayUtil.addSign(reqJSON, merkey, md5key);
		reqJSON.put("sign", sign);

		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost("https://yintong.com.cn/queryapi/supportbankquery.htm");
		httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
		try {
			RequestConfig config = RequestConfig.custom()
					.setConnectionRequestTimeout(TIMEOUT)
					.setConnectTimeout(TIMEOUT)
					.setSocketTimeout(TIMEOUT).build();
			httpPost.setConfig(config);
			StringEntity se = new StringEntity(reqJSON.toJSONString());
			se.setContentType("text/json");
			se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "text/json"));
			httpPost.setEntity(se);
			CloseableHttpResponse response = httpclient.execute(httpPost);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String content = EntityUtils.toString(entity, "UTF-8");
				JSONObject jsonResp = JSONObject.parseObject(content);
				if (!"0000".equals(jsonResp.get("ret_code").toString())) {
					logger.debug("银行状态及限额查询失败：" + jsonResp.get("ret_msg").toString());
					return null;
				}
				return jsonResp;
			}
		} catch (Exception e) {
			logger.error("银行状态及限额查询异常", e);
		}
		return null;
	}
}
